home *** CD-ROM | disk | FTP | other *** search
- /*
- TSR.C
- Original code from Al Stevens' book "Extending Turbo C Professional"
- slightly modified by Tom Grubbe
-
- NOTE: In the listing in his book, Mr. Stevens #includes "interrupt.h"
- which is nowhere to be found in the entire book! However with
- some added #defines and the IREGS structure this module compiles
- and performs without errors.
- */
-
- #include <stdio.h>
- #include <dos.h>
- #include <stdlib.h>
- #include <conio.h>
-
- #define TRUE 1
- #define FALSE 0
-
- /* --- vectors ---- */
- #define DISK 0x13
- #define INT28 0x28
- #define KYBRD 0x9
- #define CRIT 0x24
- #define DOS 0x21
- #define CTRLC 0x23
- #define CTRLBRK 0x1b
- #define TIMER 0x1c
-
- typedef struct {
- int bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,fl;
- } IREGS;
- unsigned scancode;
- unsigned keymask;
-
- extern char signature[];
- int unloading; /* TSR unload flag */
-
- static void (*UserRtn)(void); /* Pointer to user's start routine */
- static void (*InitRtn)(void); /* Pointer to user's initialization routine */
-
- /* ---- interrupt vector chains ----- */
- static void interrupt (*oldbreak)(void);
- static void interrupt (*oldctrlc)(void);
- static void interrupt (*oldtimer)(void);
- static void interrupt (*old28)(void);
- static void interrupt (*oldkb)(void);
- static void interrupt (*olddisk)(void);
- static void interrupt (*oldcrit)(void);
- /* ------ ISRs fot the TSR --------- */
- static void interrupt newtimer(void);
- static void interrupt new28(void);
- static void interrupt newkb(void);
- static void interrupt newdisk(IREGS);
- static void interrupt newcrit(IREGS);
- static void interrupt newbreak(void);
-
- static unsigned sizeprogram;/* TSR's program size */
- static unsigned dosseg; /* DOS segment address */
- static unsigned dosbusy; /* offset to InDos flag */
- static unsigned psps[2]; /* table of DOS PSP addresses */
- static int pspctr; /* # of DOS PSP addresses */
- static int diskflag; /* disk BIOS busy flag */
- static unsigned mcbseg; /* address of 1st DOS mcb */
-
- static char far *mydta; /* TSR's DTA */
- static unsigned myss; /* TSR's stack segment */
- static unsigned mysp; /* TSR's stack pointer */
- static unsigned intpsp; /* Interrupted PSP address */
- static int running; /* TSR running indicator */
- static int hotkey_flag; /* Hotkey pressed flag */
-
- /* ------ local prototypes ------- */
- void tsr(void (*FPtr)(void), void (*InitFPtr)(void));
-
- static void tsr_init(void);
- static void resinit(void);
- static void unload(void);
- static void resterm(void);
- static void pspaddr(void);
- static void dores(void);
- static void resident_psp(void);
- static void interrupted_psp(void);
- static int resident(char *signature);
- static int test_hotkeys(int ky);
-
- #define signon(s) printf("\n%s %s", signature, s);
-
- void tsr(void (*FPtr)(void), void (*InitFPtr)(void))
- {
- UserRtn = FPtr;
- InitRtn = InitFPtr;
- tsr_init();
- if (resident(signature) == FALSE) {
- /* ------- initial load of TSR program -------- */
- #ifdef DEBUG
- (*UserRtn)();
- return;
- #else
- /* ------- Terminate and Stay Resident -------- */
- (*InitRtn)(); /* user's init function */
- resinit();
- #endif
- }
- signon("is already installed.\n");
- }
-
-
-
- /* --------- initialize TSR control values ---------- */
- static void tsr_init()
- {
- unsigned es, bx;
-
- /* --------- get address of DOS busy flag --------- */
- _AH = 0x34;
- geninterrupt(DOS);
- dosseg = _ES;
- dosbusy = _BX;
- /* --------- get the seg addr of 1st DOS MCB --------- */
- _AH = 0x52;
- geninterrupt(DOS);
- es = _ES;
- bx = _BX;
- mcbseg = peek(es, bx-2);
- /* --------- get address of resident program's dta -------- */
- mydta = getdta();
- /* --------- get address of PSP in DOS 2.x --------- */
- if (_osmajor < 3)
- pspaddr();
- }
-
- /* --------- establish & declare residency ---------- */
- static void resinit()
- {
- myss = _SS;
- mysp = _SP;
- oldtimer = getvect(TIMER);
- old28 = getvect(INT28);
- oldkb = getvect(KYBRD);
- olddisk = getvect(DISK);
- /* ------- attach vectors to resident program ------- */
- setvect(TIMER, newtimer);
- setvect(KYBRD, newkb);
- setvect(INT28, new28);
- setvect(DISK, newdisk);
- /* -------- compute program's size -------- */
- sizeprogram = myss + ((mysp+50) / 16) - _psp;
- /* -------- terminate and stay resident -------- */
- keep(0, sizeprogram);
- }
-
- /* --------- break handler ----------- */
- static void interrupt newbreak()
- {
- return;
- }
-
- /* ---------- critical error ISR --------- */
- static void interrupt newcrit(IREGS ir)
- {
- ir.ax = 0; /* ignore critical errors */
- }
-
- /* -------- BIOS disk functions ISR --------- */
- static void interrupt newdisk(IREGS ir)
- {
- diskflag++;
- (*olddisk)();
- ir.ax = _AX; /* for the register returns */
- ir.cx = _CX;
- ir.dx = _DX;
- ir.fl = _FLAGS;
- --diskflag;
- }
-
- /* -------- test for the hotkey --------- */
- static int test_hotkeys(int ky)
- {
- static unsigned biosshift;
-
- biosshift = peekb(0, 0x417);
- if (ky == scancode && (biosshift & keymask) == keymask)
- hotkey_flag = !running;
- return hotkey_flag;
- }
-
- /* --------- keyboard ISR ---------- */
- static void interrupt newkb()
- {
- static int kbval;
-
- if (test_hotkeys(inportb(0x60))) {
- /* reset the keyboard */
- kbval = inportb(0x61);
- outportb(0x61, kbval | 0x80);
- outportb(0x61, kbval);
- outportb(0x20, 0x20);
- }
- else
- (*oldkb)();
- }
-
- /* --------- timer ISR ---------- */
- static void interrupt newtimer()
- {
- (*oldtimer)();
- test_hotkeys(0);
- if (hotkey_flag && peekb(dosseg, dosbusy) == 0) {
- if (diskflag == 0) {
- outportb(0x20, 0x20);
- hotkey_flag = FALSE;
- dores();
- }
- }
- }
-
- /* ---------- 0x28 ISR ---------- */
- static void interrupt new28()
- {
- (*old28)();
- if (hotkey_flag && peekb(dosseg, dosbusy) != 0) {
- hotkey_flag = FALSE;
- dores();
- }
- }
-
- /* ------ switch psp context from interrupted to TSR ------ */
- static void resident_psp()
- {
- int pp;
-
- if (_osmajor < 3) {
- /* --- save interrupted program's psp (DOS 2.x) ---- */
- intpsp = peek(dosseg, *psps);
- /* ------- set resident program's psp ------- */
- for (pp = 0; pp < pspctr; pp++)
- poke(dosseg, psps[pp], _psp);
- }
- else {
- /* ----- save interrupted program's psp ------ */
- intpsp = getpsp();
- /* ------ set resident program's psp ------- */
- _AH = 0x50;
- _BX = _psp;
- geninterrupt(DOS);
- }
- }
-
- /* -------- switch psp context from TSR to interrupted --------- */
- static void interrupted_psp()
- {
- int pp;
-
- if (_osmajor < 3) {
- /* --- reset interrupted psp (DOS 2.x) ---- */
- for (pp = 0; pp < pspctr; pp++)
- poke(dosseg, psps[pp], intpsp);
- }
- else {
- /* ------ reset interrupted psp ------- */
- _AH = 0x50;
- _BX = intpsp;
- geninterrupt(DOS);
- }
- }
-
- /* -------- execute the resident program ---------- */
- static void dores()
- {
- static char far *intdta; /* interrupted DTA */
- static unsigned intsp; /* " stack pointer */
- static unsigned intss; /* " stack segment */
- static unsigned ctrl_break; /* Ctrl-Break setting */
-
- running = TRUE; /* set TSR running metaphore */
- disable();
- intsp = _SP;
- intss = _SS;
- _SP = mysp;
- _SS = myss;
- oldcrit = getvect(CRIT);
- oldbreak = getvect(CTRLBRK);
- oldctrlc = getvect(CTRLC);
- setvect(CRIT, newcrit);
- setvect(CTRLBRK, newbreak);
- setvect(CTRLC, newbreak);
- ctrl_break = getcbrk(); /* get ctrl break setting */
- setcbrk(0); /* turn off ctrl break logic */
- intdta = getdta(); /* get interrupted dta */
- setdta(mydta); /* set resident dta */
- resident_psp(); /* swap psps */
- enable();
-
- (*UserRtn)(); /* call the TSR program here */
-
- disable();
- interrupted_psp(); /* reset interrupted psp */
- setdta(intdta); /* reset interrupted dta */
- setvect(CRIT, oldcrit); /* reset critical error */
- setvect(CTRLBRK, oldbreak);
- setvect(CTRLC, oldctrlc);
- setcbrk(ctrl_break); /* reset ctrl break */
- _SP = intsp; /* reset interrupted stack */
- _SS = intss;
- enable();
- if (unloading)
- unload();
- running = FALSE;
- }
-
- /* ------ test to see if the program is already resident -------- */
- static int resident(char *signature)
- {
- char *sg;
- unsigned df;
- unsigned blkseg, mcbs = mcbseg;
-
- df = _DS - _psp;
- /* --- walk through mcb chain & search for TSR --- */
- while (peekb(mcbs, 0) == 0x4d) {
- blkseg = peek(mcbs, 1);
- if (peek(blkseg, 0) == 0x20cd) {
- /* ---- this is a psp ---- */
- if (blkseg == _psp)
- break; /* if the transient copy */
- for (sg = signature; *sg; sg++)
- if (*sg != peekb(blkseg+df, (unsigned)sg))
- break;
- if (*sg == '\0')
- /* ---------- TSR is already resident ----------- */
- return TRUE;
- }
- mcbs += peek(mcbs, 3) + 1;
- }
- return FALSE;
- }
-
- /* --------- find address of PSP (DOS 2.x) ----------- */
- static void pspaddr()
- {
- unsigned adr = 0;
-
- disable();
- /* ------- search for matches on the psp in dos -------- */
- while (pspctr < 2 &&
- (unsigned)((dosseg<<4) + adr) < (mcbseg<<4)) {
- if (peek(dosseg, adr) == _psp) {
- /* ------ matches psp, set phoney psp ------- */
- _AH = 0x50;
- _BX = _psp + 1;
- geninterrupt(DOS);
- /* ------ did matched psp change to the phoney? ----- */
- if (peek(dosseg, adr) == _psp + 1)
- /* ------ this is a DOS 2.x psp placeholder ----- */
- psps[pspctr++] = adr;
- /* ----- reset the original psp ------ */
- _AH = 0x50;
- _BX = _psp;
- geninterrupt(DOS);
- }
- adr++;
- }
- enable();
- }
-
- /* -------- unload the rsident program --------- */
- static void unload()
- {
- if (getvect(DISK) == (void interrupt (*)()) newdisk)
- if (getvect(KYBRD) == newkb)
- if (getvect(INT28) == new28)
- if (getvect(TIMER) == newtimer) {
- resterm();
- return;
- }
- /* --- another TSR is above us, cannot unload --- */
- putch(7);
- }
-
- /* --------- TSR unload function ----------- */
- static void resterm()
- {
- unsigned mcbs = mcbseg;
- /* restore the interrupted vectors */
- setvect(TIMER, oldtimer);
- setvect(KYBRD, oldkb);
- setvect(INT28, old28);
- setvect(DISK, olddisk);
- /* obliterate the signature */
- *signature = '\0';
- /* walk through mcb chain &
- release memory owned by the TSR */
- while (peekb(mcbs, 0) == 0x4d) {
- if (peek(mcbs, 1) == _psp)
- freemem(mcbs+1);
- mcbs += peek(mcbs, 3) + 1;
- }
- }
-
-